package UI;

import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.BoundedRangeModel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.border.Border;

//@author A0103535R
/**
 * DSCommandLine extends the java swing class JTextField and implements the
 * Border and KeyListener interface. It is responsible for the display of the
 * command bar which accepts inputs from the user. It also binds the keys for
 * command history and scrolling.
 */
public class DSCommandLine extends JTextField implements Border, KeyListener {
	private static final long serialVersionUID = 1L;
	private static final Logger _logger = Logger.getLogger(DSCommandLine.class.getName());
	
	//@author A0103535R
	private static final int DOWN = 1;
	private static final int UP = -1;

	private static DSCommandLine _commandLine;

	private Vector<String> _commandHistory;
	private int _currentCommandIndex = 0;
	private String _placeHolder = "    ";
	private JScrollBar _vertScrollBar;

	/**
	 * This function is used to get the instance singleton of DSCommandLine
	 */
	public static DSCommandLine getInstance() {
		if (_commandLine == null) {
			_commandLine = new DSCommandLine(30);
		}
		return _commandLine;
	}

	/**
	 * This is the constructor of DSCommandLine.
	 * 
	 * @param column
	 * 			the number of column for width.
	 */
	private DSCommandLine(int column) {
		super(column);
		initAppearance();
		initAttributes();
		addKeyListener(this);
		_logger.setLevel(Level.OFF);
	}

	/**
	 * This function is used to initialize the appearance of DSCommandLine.
	 */
	private void initAppearance() {
		setForeground(new Color(230, 230, 230));
		setBackground(new Color(62, 70, 81, 200));
		setFont(new Font("Arial", Font.PLAIN, 14));
		setCaretColor(new Color(160, 160, 160));
		setFocusTraversalKeysEnabled(false);
		setBorder(this);
	}

	/**
	 * This function is used to initialize the attributes of DSCommandLine.
	 */
	private void initAttributes() {
		_commandHistory = new Vector<String>();
	}

	/**
	 * This function is used to get the text of place holder.
	 * 
	 * @return the text of placeHolder
	 */
	public String getPlaceHolder() {
		return this._placeHolder;
	}

	/**
	 * This function is used to set the place holder of DSCommandLine.
	 * 
	 * @param placeHolder
	 * 			the text of place holder to set
	 */
	public void setPlaceHolder(final String placeHoder) {
		this._placeHolder = placeHoder;
	}

	/**
	 * This function is used to get the user command from DSCommandLine.
	 * 
	 * @return the text of user command in DSCommandLine
	 */
	public String obtainCommand() {
		String textString = getText();
		clearText();
		updateCommandHistory(textString);
		return textString;
	}

	/**
	 * This function is used to clear both the command text and place holder.
	 */
	private void clearText() {
		setText("");
		setPlaceHolder("");
	}

	/**
	 * This function is used to add new command in command history.
	 * 
	 * @param text
	 * 			the command to add in command history.
	 */
	private void updateCommandHistory(String text) {
		_commandHistory.add(text);
		_currentCommandIndex = _commandHistory.size();
	}

	/**
	 * This function is used to show the previous user command 
	 * as text in command line.
	 */
	private void showPreviousCommand() {
		if (_currentCommandIndex > 0) {
			setText(_commandHistory.get(_currentCommandIndex - 1));
			_currentCommandIndex--;
		}

	}

	/**
	 * This function is used to show the next user command 
	 * as text in command line.
	 */
	private void showNextCommand() {
		if (hasNextCommand()) {
			_currentCommandIndex++;
			setText(_commandHistory.get(_currentCommandIndex));
		} else if (isLastCommand()) {
			_currentCommandIndex++;
			setText("");
		}
	}

	/**
	 * This function is used to check whether there is a next 
	 * command in command history.
	 * 
	 * @return the boolean result.
	 */
	private boolean hasNextCommand() {
		return (_currentCommandIndex >= 0)
				&& (_currentCommandIndex < _commandHistory.size() - 1);
	}

	/**
	 * This function is used to check current command is the 
	 * last command in command history.
	 * 
	 * @return the boolean result.
	 */
	private boolean isLastCommand() {
		return _currentCommandIndex == _commandHistory.size() - 1;
	}

	/**
	 * This function is used to append the text of place holder to 
	 * the user command and clear the place holder.
	 */
	private void completeCommand() {
		setText(getText() + _placeHolder);
		_placeHolder = "";
	}

	/**
	 * This function is used to paint the components on command line.
	 *
	 * @param pG
	 * 			the graphics for painting
	 */
	@Override
	protected void paintComponent(final Graphics pG) {
		super.paintComponent(pG);
		String textString = getText();
		int length = getFontMetrics(getFont()).stringWidth(textString);

		if (this._placeHolder.length() == 0 || textString.length() == 0) {
			return;
		}

		final Graphics2D g = (Graphics2D) pG;
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		g.setColor(new Color(140, 140, 140));
		g.drawString(_placeHolder, length + getInsets().left, pG.getFontMetrics()
				.getMaxAscent() + 6);
	}
	
	//@author generated
	@Override
	public void paintBorder(Component c, Graphics g, int x, int y, int width,
			int height) {
	}

	@Override
	public boolean isBorderOpaque() {
		return true;
	}

	@Override
	public Insets getBorderInsets(Component c) {
		return new Insets(0, 0, 0, 0);
	}

	@Override
	public Insets getInsets() {
		return new Insets(0, 5, 0, 0);
	}
	
	//@author A0094762W
	// Key Press Events
	@Override
	public void keyPressed(KeyEvent e) {
		if (tabPressed(e)) {
			System.out.printf("TAB");
			completeCommand();
		} else if (controlDownPressed(e)) {
			scrollMovement(DOWN);
		} else if (controlUpPressed(e)) {
			scrollMovement(UP);
		} else if (downPressed(e)) {
			System.out.printf("DOWN");
			showNextCommand();
		} else if (upPressed(e)) {
			System.out.printf("UP");
			showPreviousCommand();
		}
	}

	private boolean tabPressed(KeyEvent e) {
		return e.getKeyCode() == KeyEvent.VK_TAB;
	}

	private boolean controlDownPressed(KeyEvent e) {
		return e.getKeyCode() == KeyEvent.VK_DOWN && e.isShiftDown();
	}

	private boolean controlUpPressed(KeyEvent e) {
		return e.getKeyCode() == KeyEvent.VK_UP && e.isShiftDown();
	}

	private boolean downPressed(KeyEvent e) {
		return e.getKeyCode() == KeyEvent.VK_DOWN;
	}

	private boolean upPressed(KeyEvent e) {
		return e.getKeyCode() == KeyEvent.VK_UP;
	}
	
	//@author generated
	@Override
	public void keyReleased(KeyEvent e) {
	}

	@Override
	public void keyTyped(KeyEvent e) {

	}
	
	//@author A0094762W
	public void initialiseScrollPaneKeyBindings(JScrollPane scrollPane) {
		_vertScrollBar = scrollPane.getVerticalScrollBar();
	}

	private void scrollMovement(int direction) {
		BoundedRangeModel vertScrollBarModel = _vertScrollBar.getModel();
		int scrollBarValue = vertScrollBarModel.getValue();
		int scrollableIncrement = _vertScrollBar.getBlockIncrement(direction);

		if (direction == DOWN) {
			scrollBarValue += scrollableIncrement;
		} else {
			scrollBarValue -= scrollableIncrement;
		}

		vertScrollBarModel.setValue(scrollBarValue);
	}

}